iT邦幫忙

2024 iThome 鐵人賽

DAY 13
0
Software Development

我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt系列 第 13

D13 - 測試不只是找到錯誤:探索單元測試的價值與具體做法

  • 分享至 

  • xImage
  •  

哈囉,大家好!經過前面的努力,我們已經建立了個人財務管理系統的基本功能。現在,是時候來確保我們的程式碼品質了。今天,我想和大家聊聊單元測試,以及它為什麼不僅僅是為了找出錯誤,更是提升我們開發技能的重要一環。

一、為什麼要進行單元測試?

還記得有一次,我在專案中修改了一個小小的函式,結果導致其他功能無法正常運作。那時候才深刻體會到單元測試的重要性。單元測試不僅能幫助我們找到潛在的錯誤,更能:

  • 提高程式碼品質:確保每個模組都能按照預期運作。
  • 增強自信心:修改程式碼時,不用擔心會破壞現有功能。
  • 促進重構:有了測試的保護傘,重構程式碼變得更安心。

二、Laravel 的測試環境

Laravel 內建了強大的測試框架,使用 PHPUnit 進行測試。我們可以輕鬆地為應用程式撰寫和執行測試。

  • 測試目錄:tests/,包含 Feature 和 Unit 兩個子目錄。
  • 測試類別:繼承自 Tests\TestCase,讓我們可以使用 Laravel 提供的各種測試工具。

三、開始撰寫單元測試

1. 建立測試資料庫

為了不影響開發資料庫,我們需要為測試環境建立一個獨立的資料庫。

  • 建立測試資料庫:例如 personal_finance_test。
  • 設定 .env.testing 檔案:複製 .env 檔案為 .env.testing,修改資料庫連線資訊。

2. 建立測試類別

使用 Artisan 指令建立測試類別:

php artisan make:test Models/UserTest --unit

這會在 tests/Unit/Models 目錄下建立 UserTest.php。

3. 編寫測試方法

在 UserTest.php 中,我們可以開始撰寫測試。

<?php

namespace Tests\Unit\Models;

use Tests\TestCase;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

class UserTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function it_can_create_a_user()
    {
        $user = User::factory()->create([
            'username' => 'testuser',
            'email' => 'test@example.com',
        ]);

        $this->assertDatabaseHas('users', [
            'username' => 'testuser',
            'email' => 'test@example.com',
        ]);
    }
}

4. 執行測試

在終端機中執行:

php artisan test --filter=it_can_create_a_user

如果測試通過,恭喜你!我們的第一個單元測試成功了。

四、為其他模型撰寫測試

1. BankAccount 模型

建立測試類別:

php artisan make:test Models/BankAccountTest --unit

撰寫測試:

/** @test */
public function it_can_create_a_bank_account()
{
    $user = User::factory()->create();

    $bankAccount = $user->bankAccounts()->create([
        'account_name' => '薪資帳戶',
        'account_number' => '12345678',
        'bank_name' => '台灣銀行',
        'balance' => 1000.00,
    ]);

    $this->assertDatabaseHas('bank_accounts', [
        'account_name' => '薪資帳戶',
        'user_id' => $user->id,
    ]);
}

2. Category 模型

同樣地,建立 CategoryTest,撰寫測試。

3. Transaction 模型

建立 TransactionTest,撰寫測試。

五、測試模型關聯

我們可以測試模型之間的關聯是否正確。

/** @test */
public function a_user_can_have_multiple_bank_accounts()
{
    $user = User::factory()->create();
    $user->bankAccounts()->createMany([
        ['account_name' => '帳戶一', 'balance' => 500],
        ['account_name' => '帳戶二', 'balance' => 1000],
    ]);

    $this->assertCount(2, $user->bankAccounts);
}

小小補充

如果你不想在方法名稱中加上 test_ 前綴,可以使用 /** @test */ 註解來替代,它的作用是告訴PHPUnit 這個方法是一個測試
好處:
• 語義化:這樣可以讓測試方法名稱更符合自然語言,例如 user_can_be_created,而不需要強制使用test_ 前綴。
• 可讀性高:去掉了不必要的 test_ 前綴,讓測試方法的命名更簡潔和易讀。

六、使用 Factory 和 Seeder

1. 建立 Factory

Laravel 的 Factory 讓我們可以輕鬆生成測試資料。

// database/factories/UserFactory.php

public function definition()
{
    return [
        'username' => $this->faker->userName,
        'email' => $this->faker->unique()->safeEmail,
        'password' => bcrypt('password'),
    ];
}

2. 使用 Seeder

我們可以在測試中使用 Seeder 來填充資料庫。

// tests/TestCase.php

protected function setUp(): void
{
    parent::setUp();
    $this->seed();
}

七、我的心得與建議

剛開始寫單元測試時,可能會覺得多了一道工序,但長遠來看,真的省下不少時間。以下是我的一些心得:

  • 從小處開始:不用一開始就寫非常複雜的測試,先從簡單的功能測試起。
  • 持續累積:每新增一個功能,就為它撰寫相應的測試。
  • 享受過程:看到綠色的通過標記,真的會讓人心情愉悅!

單元測試還可以延伸更多的議題,但我們先保留著~

小結

透過單元測試,我們不僅能確保程式碼的品質,更能提升自己的開發技能。希望大家能夠開始嘗試在專案中加入單元測試,享受其中的樂趣與成就感。


上一篇
D12 - 掌握 Laravel 控制器,打下邏輯基礎
下一篇
D14 - 完整驗證應用程式:為重構築起無憂的安全防線
系列文
我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言